package com.neo.tiny.secrity.token;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.neo.tiny.service.RedisService;
import com.neo.tiny.token.store.AccessTokenInfo;
import com.neo.tiny.token.store.OAuth2AccessToken;
import com.neo.tiny.token.store.TokenStore;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author yqz
 * @Description
 * @CreateDate 2022/11/11 13:31
 */
@Slf4j
@Component
@AllArgsConstructor
public class RedisTokenStore implements TokenStore {

    private final RedisService redisService;

    private final static String OAUTH_TOKEN_PRE = "oauth:";

    @Override
    public void storeAccessToken(OAuth2AccessToken token) {

        String key = generateAccessTokenKey(token.getClientId(), token.getUserType(), token.getUserId(), token.getAccessToken());

        long seconds = Duration.between(LocalDateTime.now(), token.getExpiresTime()).getSeconds();
        redisService.set(key, token, seconds, TimeUnit.SECONDS);
    }

    /**
     * {@link com.neo.tiny.oauth.enums.LogoutScope}
     *
     * @param token token
     */
    @Override
    public void removeAccessToken(OAuth2AccessToken token) {
        String clientId = StrUtil.isBlank(token.getClientId()) ? "*" : token.getClientId();
        String key = generateAccessTokenKeyPrefix(clientId, token.getUserType(), token.getUserId());
        Set<String> keys = redisService.keys(key + "*");
        //一次退出，则退出所有
        redisService.del(keys);
    }

    @Override
    public OAuth2AccessToken getAccessToken(String accessToken) {
        Set<String> keys = redisService.keys(OAUTH_TOKEN_PRE + "*" + accessToken);

        if (CollUtil.isEmpty(keys)) {
            return null;
        }
        Optional<AccessTokenInfo> oAuth2AccessToken = redisService.get(keys.stream().findFirst().get(), AccessTokenInfo.class);

        return oAuth2AccessToken.orElse(null);
    }

    @Override
    public OAuth2AccessToken getAccessTokenWithUserInfo(String clientId, Integer userType, Long userId) {
        String keyPrefix = generateAccessTokenKeyPrefix(clientId, userType, userId);

        Set<String> keys = redisService.keys(keyPrefix + "*");
        log.info("根据用户信息查询token：{}", keys);
        if (CollUtil.isNotEmpty(keys)) {
            Optional<String> first = keys.stream().findFirst();
            Optional<AccessTokenInfo> accessTokenInfo = redisService.get(first.get(), AccessTokenInfo.class);
            return accessTokenInfo.orElse(null);
        }
        return null;
    }


    /**
     * 生成统一accessToken key前缀
     *
     * @param clientId    客户端id
     * @param userType    用户类型
     * @param userId      用户id
     * @param accessToken token
     * @return key
     */
    private String generateAccessTokenKey(String clientId, Integer userType, Long userId, String accessToken) {
        return generateAccessTokenKeyPrefix(clientId, userType, userId) + accessToken;
    }

    private String generateAccessTokenKeyPrefix(String clientId, Integer userType, Long userId) {
        return OAUTH_TOKEN_PRE + clientId + StrUtil.COLON +
                userType + StrUtil.COLON + userId + StrUtil.COLON;
    }
}
